/*
 * Copyright (c) 2010, artur
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * kiddie-os
 * Created on: 14.04.2010
 *     Author: artur
 *
 */

#include "Shell.h"
#include <hw/Platform.h>
#include <irq/EventController.h>
#include <sys/types.h>
#include <sys/sched/task/Task.h>
#include <util/list.h>
#include <sys/sched/Scheduler.h>

extern kernel::Platform hwplatform;

#define SHIFT_BIT	0x1
#define ALT_BIT		0x2
#define CTRL_BIT	0x4
#define TAB_BIT		0x8
#define BS_BIT		0x10

extern util::list<kernel::sys::sched::task::Task *> running;

#if 1 // this macro is for editor's folding only
unsigned char util::Shell::ascii[] =
  { 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
      '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
      0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\',
      'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, };
#endif

namespace util
{
  /**
   *
   */
  Shell::Shell()
  {
    Greeting = (char *) "kiddie~$ ";
    kbitmap = 0;
    // clear cmd buffer
    for (int i = 0; i < sizeof(buffer); i++)
      buffer[i] = 0;
    bufpos = 0;
  }

  /**
   *
   */
  Shell::~Shell()
  {
  }

  /**
   *
   */
  void
  Shell::entry(void*)
  {
  }

  /**
   *
   */
  void
  Shell::onEvent(kernel::irq::Event *evt)
  {
    unsigned char sc;
    bool rel = false;
    unsigned char c;

    c = __inb((int) phys_to_virt(CONFIG_KBD_BASE));
    sc = c;

    // TODO This code must use map tables
    // instead of to be hardcoded
    if (c & 0x80)
      {
        c &= (0x80 - 1);
        this->release -= c;
        rel = true;
      }
    else
      {
        this->release += c;
      }

    switch (c)
      {
    case 0x2a: // L_SHIFT
    case 0x36: // R_SHIFT
      if (rel)
        {
          this->kbitmap &= ~SHIFT_BIT;
        }
      else
        {
          this->kbitmap |= SHIFT_BIT;
        }
      break;

    case 0x38: // L_ALT
    case 0xe0: // R_ALT
      if (rel)
        this->kbitmap &= ~ALT_BIT;
      else
        this->kbitmap |= ALT_BIT;
      break;

    case 0x1d: // L_CTRL
      if (rel)
        this->kbitmap &= ~CTRL_BIT;
      else
        this->kbitmap |= CTRL_BIT;
      break;

    default:
      if (!rel)
        goto no_service_code;
      break;
      }

    service_code: return;

    no_service_code: c = ascii[(int) c];

    // If user typed enter
    if (sc == 0x1c) // Enter
      {
        ::hwplatform.console << "\n";
        doCmdString( buffer);
        // clear cmd buffer
        for (int i = 0; i < SHELL_BUFSIZ; i++)
          buffer[i] = 0;
        bufpos = 0;
        // show greeting
        ::hwplatform.console << this->Greeting;
        return;
      }

    // Erase last character
    if (c == '\b')
      {
        if (bufpos > 0)
          {
            ::hwplatform.console << (char) c;
            buffer[--bufpos] = 0;
          }
        return;
      }

    if (this->kbitmap & SHIFT_BIT)
      {
        if ('0' < c && c <= '?')
          c -= 0x10;
        else if ('a' <= c && c <= 'z')
          c -= 0x20;
        else if ('[' <= c && c <= '_')
          c += 0x20;
      }

    // Print character if the buffer is not full
    if (!rel)
      {
        if (bufpos < SHELL_BUFSIZ)
          {
            buffer[bufpos++] = (char) c;
            ::hwplatform.console << (char) c;
          }
      }

  }

  /**
   *
   */
  IRunnable &
  Shell::operator <<(kernel::cmd::CMD cmd)
  {
    switch (cmd)
      {
    case kernel::cmd::INIT:
      break;
    default:
      break;
      }
    return *this;
  }

  /**
   *
   */
  Capabilities
  Shell::getCaps(void)
  {
    return caps;
  }

  /**
   *
   */
  IRunnable *__init Shell::newInstance(Capabilities &syscaps)
    {
      ::hwplatform.console << "[ Shell ] newInstance()\n";
      Shell *sh = new Shell();
      return sh;
    }

  /**
   *
   */
  void
  Shell::doCmdString(const char *str)
  {

    if (!util::strncmp(str, "ps", 2))
      {
        kernel::sys::sched::task::Task *tsk;
        // TODO Create list iterator here!!!
        util::__list_element *_el = ::running.head;
        do
          {
            if (!_el)
              break;
            tsk = (kernel::sys::sched::task::Task *) _el->data;
            if (!tsk)
              break;
            ::hwplatform.console << "Kernel task entry=" << (int) tsk->entry
                << "\n";
            _el = _el->next;
          }
        while (_el != ::running.head);
        ::hwplatform.console << "Task number => " << running.size << "\n";
      }
    if (!util::strncmp(str, "cmdline", sizeof("cmdline")))
      {
        ::hwplatform.console << (char *) phys_to_virt(
            ::hwplatform.bootInfo->cmdline) << "\n";
      }
    if (!util::strncmp(str, "killnext", sizeof("killnext")))
      {
        ::hwplatform.eventController << kernel::irq::EventController::OFF;
        kernel::sys::sched::task::Task *t = ::running.next();
        *::hwplatform.scheduler -= t;
        ::hwplatform.eventController << kernel::irq::EventController::OFF;
        // ::current = ::trash;
      }
  }

}

/**
 *
 */
::util::Capabilities util::Shell::caps =
  { tags: "shell"};

/**
 *
 */
imodule_init(shellInit,util::Shell::newInstance)
